#include "component_container.h"
#include <i_log.h>
#include <i_component.h>
#include <i_component_holder.h>

CComponentContainer::CComponentContainer(CComponentFactoryCenter& center)
	: m_center(center), m_running(false)
{
}

err_code_t CComponentContainer::Register(IComponentHolder& holder)
{
	if (m_running)
		return ECOMPONENT_STATE_ERR;

	_ComponentDetail& detail = _MutableDetail(holder.GetComponentName());
	if (!detail.holders.insert(&holder).second)
		return ECOMPONENT_DUPLICATE;

	if (detail.component_ptr)
		holder.OnComponentInstall(*detail.component_ptr);

	return ECOMPONENT_OK;
}

void CComponentContainer::Unregister(IComponentHolder& holder)
{
	auto it = m_component_detail.find(holder.GetComponentName());
	if (it == m_component_detail.end())
		return;

	auto detail = _GetDetail(it->second);
	if(detail != nullptr)
		detail->holders.erase(&holder);
}

void CComponentContainer::_Install()
{
	for (auto& detail : m_components)
		_Install(detail);
}

void CComponentContainer::_Install(_ComponentDetail& detail)
{
	if (!detail.component_ptr)
		return;

	detail.component_ptr->Install(*this, detail.com_id);
	for (auto* holder : detail.holders)
		holder->OnComponentInstall(*detail.component_ptr);
}

void CComponentContainer::_Uninstall(_ComponentDetail& detail)
{
	if (!detail.component_ptr)
		return;

	detail.component_ptr->Uninstall();
	for (auto* holder : detail.holders)
		holder->OnComponentUninstall(*detail.component_ptr);
}

void CComponentContainer::_Uninstall()
{
	for (auto& detail : m_components)
		_Uninstall(detail);
}

CComponentContainer::_ComponentDetail* CComponentContainer::_GetDetail(size_t com_id)
{
	return com_id != 0 && com_id <= m_components.size() ? &m_components[com_id - 1] : nullptr;
}

CComponentContainer::_ComponentDetail& CComponentContainer::_MutableDetail(const std::string& name)
{
	_ComponentDetail* detail = nullptr;
	auto& com_id = m_component_detail[name];
	if (com_id != 0)
		detail = _GetDetail(com_id);

	if (detail == nullptr)
	{
		m_components.resize(m_components.size() + 1);
		detail = &m_components.back();
		com_id = detail->com_id = m_components.size();
		detail->name = name;
	}

	return *detail;
}

err_code_t CComponentContainer::Start()
{
	if (m_running)
		return ECOMPONENT_DUPLICATE;

	m_center.CreateComponents(*this);

	_Install();

	static const int MAX_START_ROUND = 10;
	int round = 0;
	while (round++ < MAX_START_ROUND)
	{
		int pending_count = 0;
		for (auto& detail : m_components)
		{
			if(!detail.component_ptr)
				continue;

			auto err_code = detail.component_ptr->Start();
			if (err_code == ECOMPONENT_OK)
				continue;
			else if (err_code != ECOMPONENT_WAIT)
			{
				SPDLOG_ERROR("component {} start error {}", detail.name, err_code);
				return ECOMPONENT_FAIL;
			}

			++pending_count;
		}

		if (pending_count == 0)
		{
			m_running = true;
			return ECOMPONENT_OK;
		}
	}
	
	return ECOMPONENT_TIMEOUT;
}

void CComponentContainer::Stop()
{
	if (!m_running)
		return;

	for (auto& detail : m_components)
	{
		if(!detail.component_ptr)
			continue;

		detail.component_ptr->Stop();
	}
	
	_Uninstall();
	m_running = false;
}

void CComponentContainer::Update()
{
	auto msg_list = m_msg_list.GetList();
	if (!msg_list)
		return;

	while (!msg_list->empty())
	{
		_ComponentMsg& msg = msg_list->front();
		auto ptr = _GetDetail(msg.com_id);
		if (ptr != nullptr && ptr->component_ptr)
			msg.fn(ptr->component_ptr);

		msg_list->pop_front();
	}
}

void CComponentContainer::PostComponent(ComID com_id, const CComponentCallBackFn& cb)
{
	m_msg_list.Emplace(com_id, cb);
}

void CComponentContainer::RemoveComponent(ComID com_id)
{
	auto detail = _GetDetail(com_id);
	if (detail == nullptr)
		return;

	if (!detail->component_ptr)
		return;

	if (detail->component_ptr->GetStatus() == ECOMPONENT_STATUS_RUNNING)
		detail->component_ptr->Stop();

	_Uninstall(*detail);
}

void CComponentContainer::AppendComponent(const std::string& name, const CComponentFactoryPtr& ptr)
{
	auto& detail = _MutableDetail(name);
	if (detail.component_ptr)
		return;
	
	detail.component_ptr = ptr->CreateInstance();
}
